{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 写在前面的话\n",
    "\n",
    "读书的时候上过《设计模式》这一门课，当时使用的教材是程杰老师的《大话设计模式》，使用的语言是C#，学过课程之后初期深感面向对象思想的伟大，但是很少应用到实际开发中。后来我接触了Python，现在工作中用到最多的也是Python，或许是因为Python的便利性，我写的很多脚本/程序都还是面向过程编程，缺少面向对象的思想在里边。因此，我打算重读程杰老师的《大话设计模式》并用Python进行实践。\n",
    "\n",
    "## 题目\n",
    "\n",
    "用一种面向对象语言实现一个计算器控制台程序, 要求输入两个数和运算符号(+-*/), 得到结果.\n",
    "\n",
    "## 基础版本"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input a number:16\n",
      "input a operater(+ - * /):*\n",
      "input a number:2\n",
      "32\n"
     ]
    }
   ],
   "source": [
    "a = int(input(\"input a number:\"))\n",
    "b = str(input(\"input a operater(+ - * /):\"))\n",
    "c = int(input(\"input a number:\"))\n",
    "\n",
    "if b == \"+\":\n",
    "    print(a+c)\n",
    "elif b == \"-\":\n",
    "    print(a-c)\n",
    "elif b == \"*\":\n",
    "    print(a*c)\n",
    "else b == \"/\":\n",
    "    print(a/c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "1. 变量命名不规范\n",
    "2. 无用的if条件判断太多\n",
    "3. 除法运算中未考虑第二个数字为0的情况\n",
    "\n",
    "## 改进版本1.0——规范代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input a number:12\n",
      "input a operater(+ - * /):/\n",
      "input a number:0\n",
      "With operator '/', the second number can not be zero.\n"
     ]
    }
   ],
   "source": [
    "number_a = int(input(\"input a number:\"))\n",
    "operator = str(input(\"input a operater(+ - * /):\"))\n",
    "number_b = int(input(\"input a number:\"))\n",
    "\n",
    "if operator == \"+\":\n",
    "    print(number_a + number_b)\n",
    "elif operator == \"-\":\n",
    "    print(number_a - number_b)\n",
    "elif operator == \"*\":\n",
    "    print(number_a * number_b)\n",
    "elif operator == \"/\":\n",
    "    if number_b != 0:\n",
    "        print(number_a / number_b)\n",
    "    else:\n",
    "        print(\"With operator '/', the second number can not be zero.\")\n",
    "else:\n",
    "    print(\"Wrong operator.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "1. 没有使用面向对象的思想\n",
    "2. 只满足当前需求, 不易维护, 不易扩展, 不易复用, 不够灵活\n",
    "\n",
    "### 为什么活字印刷术能位列四大发明？主要是其方法的思想。\n",
    "1. 文章改字方便, 可维护\n",
    "2. 一个字可以重复使用, 可复用\n",
    "3. 文章加字容易, 可扩展\n",
    "4. 文章改版只需移动活字, 灵活性好\n",
    "\n",
    "### 复制?复用?\n",
    "如果做一个带图形化界面的计算器，上边的代码需要再写一次。为了避免这样，需要将`业务逻辑`与`界面逻辑`分开，降低耦合度。\n",
    "\n",
    "### 改进版本2.0——利用封装解耦"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input a number:12\n",
      "input a operater(+ - * /):+\n",
      "input a number:12\n",
      "24\n"
     ]
    }
   ],
   "source": [
    "class Operation():\n",
    "\n",
    "    def __init__(self):\n",
    "        self.result = None\n",
    "\n",
    "    def get_result(self, number_a, number_b, operator):\n",
    "        if operator == \"+\":\n",
    "            self.result = number_a + number_b\n",
    "        elif operator == \"-\":\n",
    "            self.result = number_a - number_b\n",
    "        elif operator == \"*\":\n",
    "            self.result = number_a * number_b\n",
    "        elif operator == \"/\":\n",
    "            if number_b != 0:\n",
    "                self.result = number_a / number_b\n",
    "            else:\n",
    "                print(\"With operator '/', the second number can not be zero.\")\n",
    "        else:\n",
    "            print(\"Wrong operator.\")\n",
    "        return self.result\n",
    "\n",
    "number_a = int(input(\"input a number:\"))\n",
    "operator = str(input(\"input a operater(+ - * /):\"))\n",
    "number_b = int(input(\"input a number:\"))\n",
    "\n",
    "operation = Operation()\n",
    "print(operation.get_result(number_a, number_b, operator))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "1. 仅仅用到了封装, 还没用到继承和多态。\n",
    "\n",
    "### 紧耦合?松耦合?\n",
    "如果要支持一个开根号运算，上边的代码需要改动包括加减乘除在内的`get_result`函数，应该将加减乘除运算分离, 修改其中一个不影响其他的几个。那么就需要定义一个Operator基类, 将`get_result`定义为虚函数，然后通过继承和多态，分别实现加减乘除四个子类，每个子类中定义虚函数的实现逻辑。\n",
    "\n",
    "参考资料: [Python中的多态与虚函数](https://blog.csdn.net/Tony_Wong/article/details/39638887)\n",
    "\n",
    "## 改进版本3.0——简单工厂模式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class Operation():\n",
    "    __metaclass__ = ABCMeta\n",
    "\n",
    "    def __init__(self):\n",
    "        self.result = None\n",
    "\n",
    "    @abstractmethod\n",
    "    def get_result(self):\n",
    "        pass\n",
    "\n",
    "    \n",
    "class AddOperation(Operation):\n",
    "    \n",
    "    def get_result(self, number_a, number_b):\n",
    "        self.result = number_a + number_b\n",
    "        return self.result\n",
    "\n",
    "    \n",
    "class SubOperation(Operation):\n",
    "\n",
    "    def get_result(self, number_a, number_b):\n",
    "        self.result = number_a - number_b\n",
    "        return self.result\n",
    "\n",
    "    \n",
    "class MulOperation(Operation):\n",
    "\n",
    "    def get_result(self, number_a, number_b):\n",
    "        self.result = number_a * number_b\n",
    "        return self.result\n",
    "    \n",
    "    \n",
    "class DivOperation(Operation):\n",
    "\n",
    "    def get_result(self, number_a, number_b):\n",
    "        if number_b == 0:\n",
    "            print(\"With operator '/', the second number can not be zero.\")\n",
    "            return self.result\n",
    "        self.result = number_a / number_b\n",
    "        return self.result"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 如何实例化?——简单工厂模式\n",
    "现在加减乘除的实现逻辑已经进一步隔离，之后即使增加一个开根号运算符，也和加减乘除无关。那么如何去实例化这些类呢？可以用`简单工厂模式`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "class OperationFactory():\n",
    "    \n",
    "    @classmethod\n",
    "    def create_operate(self, operator):\n",
    "        oper = None\n",
    "        if operator == \"+\":\n",
    "            oper = AddOperation()\n",
    "        elif operator == \"-\":\n",
    "            oper = SubOperation()\n",
    "        elif operator == \"*\":\n",
    "            oper = MulOperation()\n",
    "        elif operator == \"/\":\n",
    "            oper = DivOperation()\n",
    "        else:\n",
    "            print(\"Wrong operator.\")\n",
    "        return oper"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过上边的简单工厂，输入运算符号，就可以实例化出对应的对象。下边是客户端的代码。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input a number:12\n",
      "input a operater(+ - * /):-\n",
      "input a number:12\n",
      "0\n"
     ]
    }
   ],
   "source": [
    "number_a = int(input(\"input a number:\"))\n",
    "operator = str(input(\"input a operater(+ - * /):\"))\n",
    "number_b = int(input(\"input a number:\"))\n",
    "\n",
    "oper = OperationFactory.create_operate(operator)\n",
    "print(oper.get_result(number_a, number_b))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "1. 业务逻辑与界面逻辑隔离，不关心是控制台程序还是GUI程序\n",
    "2. 不同运算逻辑隔离，一个运算符的增删改操作不会影响其他运算\n",
    "3. 面向对象思想的封装，继承，多态都有所体现\n",
    "4. 易维护，易扩展，易复用"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
